{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Matplotlib 最具价值的50个可视化项目\n",
    "本文总结了 Matplotlib 以及 Seaborn 用的最多的50个图形，掌握这些图形的绘制，对于数据分析的可视化有莫大的作用，强烈推荐大家阅读后续内容。\n",
    "来源：https://www.machinelearningplus.com/plots/top-50-matplotlib-visualizations-the-master-plots-python/\n",
    "\n",
    "介绍\n",
    "这些图表根据可视化目标的7个不同情景进行分组。\n",
    "\n",
    "有效图表的重要特征：\n",
    "\n",
    "在不歪曲事实的情况下传达正确和必要的信息。\n",
    "设计简单，您不必太费力就能理解它。\n",
    "从审美角度支持信息而不是掩盖信息。\n",
    "信息没有超负荷。\n",
    "准备工作\n",
    "在代码运行前先引入下面的设置内容。 当然，单独的图表，可以重新设置显示要素。\n",
    "其中设置了不显示warnings（由于版本变动导致的函数变动），图片以嵌入式方式展示。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3.8.4\n",
      "0.12.2\n"
     ]
    }
   ],
   "source": [
    "\n",
    "\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "import warnings; warnings.filterwarnings(action='once')\n",
    "\n",
    "# set the  fontsize and some other elements\n",
    "large = 22; med = 16; small = 12\n",
    "params = {'axes.titlesize': large,\n",
    "          'legend.fontsize': med,\n",
    "          'figure.figsize': (16, 10),\n",
    "          'axes.labelsize': med,\n",
    "          'axes.titlesize': med,\n",
    "          'xtick.labelsize': med,\n",
    "          'ytick.labelsize': med,\n",
    "          'figure.titlesize': large}\n",
    "plt.rcParams.update(params)\n",
    "# plt.style.use('seaborn-whitegrid')\n",
    "sns.set_style(\"white\")\n",
    "%matplotlib inline\n",
    "\n",
    "# Print Version\n",
    "print(mpl.__version__)  \n",
    "print(sns.__version__)  \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1、关联 （Correlation）\n",
    "关联图表用于可视化2个或更多变量之间的关系。 也就是说，一个变量如何相对于另一个变化。\n",
    "\n",
    "散点图（Scatter plot）\n",
    "散点图是用于研究两个变量之间关系的经典的和基本的图表。 如果数据中有多个组，则可能需要以不同颜色可视化每个组。 在 matplotlib 中，您可以使用 plt.scatter() 方便地执行此操作。\n",
    "np.unique():列表元素去重\n",
    "当前的图表和子图可以使用plt.gcf()和plt.gca()获得,分别表示\"Get Current Figure\"和\"Get Current Axes\"，这样可以方便的设置x，y轴显示范围及标签。\n",
    "enumerate(sequence, [start=0])函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列，同时列出数据和数据下标，一般用在 for 循环当中。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# Import dataset \n",
    "midwest = pd.read_csv('midwest_filter.csv')\n",
    "\n",
    "# Prepare Data \n",
    "# Create as many colors as there are unique midwest['category']\n",
    "categories = np.unique(midwest['category'])\n",
    "colors = [plt.cm.tab10(i/float(len(categories)-1)) for i in range(len(categories))]\n",
    "\n",
    "# Draw Plot for Each Category\n",
    "plt.figure(figsize=(16, 10), dpi= 80, facecolor='w', edgecolor='k')\n",
    "\n",
    "for i, category in enumerate(categories):\n",
    "    plt.scatter('area', 'poptotal', data=midwest.loc[midwest.category==category, :], s=20, cmap=colors[i], label=str(category))\n",
    "\n",
    "# Decorations\n",
    "plt.gca().set(xlim=(0.0, 0.1), ylim=(0, 90000),xlabel='Area', ylabel='Population')\n",
    "\n",
    "plt.xticks(fontsize=12)\n",
    "plt.yticks(fontsize=12)\n",
    "plt.title(\"Scatterplot of Midwest Area vs Population\", fontsize=22)\n",
    "plt.legend(fontsize=12)    \n",
    "plt.show()    \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "d:\\anaconda3\\lib\\site-packages\\ipykernel\\ipkernel.py:287: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n",
    "  and should_run_async(code)\n",
    "\n",
    "带边界的气泡图（Bubble plot with Encircling）\n",
    "有时，您希望在边界内显示一组点以强调其重要性。 在这个例子中，你从数据框中获取记录，并用下面代码中描述的 encircle() 来使边界显示出来。\n",
    "np.r_是按列连接两个矩阵，就是把两矩阵上下相加，要求列数相等，类似于pandas中的concat()。\n",
    "np.c_是按行连接两个矩阵，就是把两矩阵左右相加，要求行数相等，类似于pandas中的merge()。\n",
    "ConvexHull：给定二维平面上的点集，凸包就是将最外层的点连接起来构成的凸多边型，它能包含点集中所有的点。\n",
    "很不幸的是竟然报错了qaq，经过分析发现，由于电脑用户名取成了中文，缓存的路径就无法被识别了。可是Python3是utf-8编码的，为什么会不支持中文？可能是scipy默认用了ascii码形式读取的文件路径，由于scipy调用c++的命令，源码大部分被封装，无法更改。win10的用户名只有在登陆Administrator时才能更改原来的用户组名称，而且用户名涉及很多环境变量和注册表信息，不敢作死，只能认栽了。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "from matplotlib import patches\n",
    "from scipy.spatial import ConvexHull\n",
    "import warnings; warnings.simplefilter('ignore')\n",
    "sns.set_style(\"white\")\n",
    "\n",
    "# Step 1: Prepare Data\n",
    "midwest = pd.read_csv(\"midwest_filter.csv\")\n",
    "\n",
    "# As many colors as there are unique midwest['category']\n",
    "categories = np.unique(midwest['category'])\n",
    "colors = [plt.cm.tab10(i/float(len(categories)-1)) for i in range(len(categories))]\n",
    "\n",
    "# Step 2: Draw Scatterplot with unique color for each category\n",
    "fig = plt.figure(figsize=(16, 10), dpi= 80, facecolor='w', edgecolor='k')    \n",
    "\n",
    "for i, category in enumerate(categories):\n",
    "    plt.scatter('area', 'poptotal', data=midwest.loc[midwest.category==category, :], \n",
    "                s='dot_size', cmap=colors[i], label=str(category), edgecolors='black', linewidths=.5)\n",
    "\n",
    "# Step 3: Encircling\n",
    "# https://stackoverflow.com/questions/44575681/how-do-i-encircle-different-data-sets-in-scatter-plot\n",
    "def encircle(x,y, ax=None, **kw):\n",
    "    if not ax: ax=plt.gca()\n",
    "    p = np.c_[x,y]\n",
    "    hull = ConvexHull(p)\n",
    "    poly = plt.Polygon(p[hull.vertices,:], **kw)\n",
    "    ax.add_patch(poly)\n",
    "\n",
    "# Select data to be encircled\n",
    "midwest_encircle_data = midwest.loc[midwest.state=='IN', :]                         \n",
    "\n",
    "# Draw polygon surrounding vertices    \n",
    "encircle(midwest_encircle_data.area, midwest_encircle_data.poptotal, ec=\"k\", fc=\"gold\", alpha=0.1)\n",
    "encircle(midwest_encircle_data.area, midwest_encircle_data.poptotal, ec=\"firebrick\", fc=\"none\", linewidth=1.5)\n",
    "\n",
    "# Step 4: Decorations\n",
    "plt.gca().set(xlim=(0.0, 0.1), ylim=(0, 90000),xlabel='Area', ylabel='Population')\n",
    "\n",
    "plt.xticks(fontsize=12)\n",
    "plt.yticks(fontsize=12)\n",
    "plt.title(\"Bubble Plot with Encircling\", fontsize=22)\n",
    "plt.legend(fontsize=12)    \n",
    "plt.show()    \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "带线性回归最佳拟合线的散点图 （Scatter plot with linear regression line of best fit）\n",
    "如果你想了解两个变量如何相互改变，那么最佳拟合线就是常用的方法。 下图显示了数据中各组之间最佳拟合线的差异。 要禁用分组并仅为整个数据集绘制一条最佳拟合线，请从下面的sns.lmplot()调用中删除hue ='cyl'参数。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "# Import Data\n",
    "df = pd.read_csv(\"mpg_ggplot2.csv\")\n",
    "df_select = df.loc[df.cyl.isin([4,8]), :]\n",
    "\n",
    "# Plot\n",
    "sns.set_style(\"white\")\n",
    "gridobj = sns.lmplot(x=\"displ\", y=\"hwy\", hue=\"cyl\", data=df_select, \n",
    "                     aspect=1.6, robust=True, palette='tab10', \n",
    "                     scatter_kws=dict(s=60, linewidths=.7, edgecolors='black'))\n",
    "\n",
    "# Decorations\n",
    "gridobj.set(xlim=(0.5, 7.5), ylim=(0, 50))\n",
    "plt.title(\"Scatterplot with line of best fit grouped by number of cylinders\", fontsize=20)\n",
    "plt.show()\n",
    "\n",
    "针对每列绘制线性回归线\n",
    "\n",
    "或者，可以在其每列中显示每个组的最佳拟合线。 可以通过在 sns.lmplot() 中设置 col=groupingcolumn 参数来实现，如下：\n",
    "\n",
    "# Import Data\n",
    "df = pd.read_csv(\"mpg_ggplot2.csv\")\n",
    "df_select = df.loc[df.cyl.isin([4,8]), :]\n",
    "\n",
    "# Each line in its own column\n",
    "sns.set_style(\"white\")\n",
    "gridobj = sns.lmplot(x=\"displ\", y=\"hwy\", \n",
    "                     data=df_select, \n",
    "                     robust=True, \n",
    "                     palette='Set1', \n",
    "                     col=\"cyl\",\n",
    "                     scatter_kws=dict(s=60, linewidths=.7, edgecolors='black'))\n",
    "\n",
    "# Decorations\n",
    "gridobj.set(xlim=(0.5, 7.5), ylim=(0, 50))\n",
    "plt.show()\n",
    "\n",
    "抖动图 （Jittering with stripplot）\n",
    "通常，多个数据点具有完全相同的 X 和 Y 值。 结果，多个点绘制会重叠并隐藏。 为避免这种情况，请将数据点稍微抖动，以便您可以直观地看到它们。 使用 seaborn 的 stripplot() 很方便实现这个功能。\n",
    "\n",
    "# Import Data\n",
    "df = pd.read_csv(\"mpg_ggplot2.csv\")\n",
    "\n",
    "# Draw Stripplot\n",
    "fig, ax = plt.subplots(figsize=(16,10), dpi= 80)    \n",
    "sns.stripplot(df.cty, df.hwy, jitter=0.25, size=8, ax=ax, linewidth=.5)\n",
    "\n",
    "# Decorations\n",
    "plt.title('Use jittered plots to avoid overlapping of points', fontsize=22)\n",
    "plt.show()\n",
    "\n",
    "计数图 （Counts Plot）\n",
    "避免点重叠问题的另一个选择是增加点的大小，这取决于该点中有多少点。 因此，点的大小越大，其周围的点的集中度越高。\n",
    "groupby操作涉及拆分对象，应用函数和组合结果的某种组合。这可用于对这些组上的大量数据和计算操作进行分组。\n",
    "reset_index重置DataFrame的索引，并使用默认值。如果DataFrame具有MultiIndex，则此方法可以删除一个或多个级别。\n",
    "\n",
    "# Import Data\n",
    "df = pd.read_csv(\"mpg_ggplot2.csv\")\n",
    "df_counts = df.groupby(['hwy', 'cty']).size().reset_index(name='counts')\n",
    "\n",
    "# Draw Stripplot\n",
    "fig, ax = plt.subplots(figsize=(16,10), dpi= 80)\n",
    "\n",
    "#sns.stripplot(df_counts.cty, df_counts.hwy, size=df_counts.counts*2, ax=ax)\n",
    "sns.stripplot(df_counts.cty, df_counts.hwy, ax=ax)\n",
    "\n",
    "# Decorations\n",
    "plt.title('Counts Plot - Size of circle is bigger as more points overlap', fontsize=22)\n",
    "plt.show()\n",
    "\n",
    "边缘直方图 （Marginal Histogram）\n",
    "边缘直方图具有沿 X 和 Y 轴变量的直方图。 这用于可视化 X 和 Y 之间的关系以及单独的 X 和 Y 的单变量分布。 这种图经常用于探索性数据分析（EDA）。\n",
    "\n",
    "# Import Data\n",
    "df = pd.read_csv(\"mpg_ggplot2.csv\")\n",
    "\n",
    "# Create Fig and gridspec\n",
    "fig = plt.figure(figsize=(16, 10), dpi= 80)\n",
    "grid = plt.GridSpec(4, 4, hspace=0.5, wspace=0.2)\n",
    "\n",
    "# Define the axes\n",
    "ax_main = fig.add_subplot(grid[:-1, :-1])\n",
    "ax_right = fig.add_subplot(grid[:-1, -1], xticklabels=[], yticklabels=[])\n",
    "ax_bottom = fig.add_subplot(grid[-1, 0:-1], xticklabels=[], yticklabels=[])\n",
    "\n",
    "# Scatterplot on main ax\n",
    "ax_main.scatter('displ', 'hwy', s=df.cty*4, c=df.manufacturer.astype('category').cat.codes, alpha=.9, data=df, cmap=\"tab10\", edgecolors='gray', linewidths=.5)\n",
    "\n",
    "# histogram on the right\n",
    "ax_bottom.hist(df.displ, 40, histtype='stepfilled', orientation='vertical', color='deeppink')\n",
    "ax_bottom.invert_yaxis()\n",
    "\n",
    "# histogram in the bottom\n",
    "ax_right.hist(df.hwy, 40, histtype='stepfilled', orientation='horizontal', color='deeppink')\n",
    "\n",
    "# Decorations\n",
    "ax_main.set(title='Scatterplot with Histograms \\n displ vs hwy', xlabel='displ', ylabel='hwy')\n",
    "ax_main.title.set_fontsize(20)\n",
    "for item in ([ax_main.xaxis.label, ax_main.yaxis.label] + ax_main.get_xticklabels() + ax_main.get_yticklabels()):\n",
    "    item.set_fontsize(14)\n",
    "\n",
    "xlabels = ax_main.get_xticks().tolist()\n",
    "ax_main.set_xticklabels(xlabels)\n",
    "plt.show()\n",
    "\n",
    "边缘箱形图 （Marginal Boxplot）\n",
    "边缘箱图与边缘直方图具有相似的用途。 然而，箱线图有助于精确定位 X 和 Y 的中位数、第25和第75百分位数。\n",
    "\n",
    "# Import Data\n",
    "df = pd.read_csv(\"mpg_ggplot2.csv\")\n",
    "\n",
    "# Create Fig and gridspec\n",
    "fig = plt.figure(figsize=(16, 10), dpi= 80)\n",
    "grid = plt.GridSpec(4, 4, hspace=0.5, wspace=0.2)\n",
    "\n",
    "# Define the axes\n",
    "ax_main = fig.add_subplot(grid[:-1, :-1])\n",
    "ax_right = fig.add_subplot(grid[:-1, -1], xticklabels=[], yticklabels=[])\n",
    "ax_bottom = fig.add_subplot(grid[-1, 0:-1], xticklabels=[], yticklabels=[])\n",
    "\n",
    "# Scatterplot on main ax\n",
    "ax_main.scatter('displ', 'hwy', s=df.cty*5, c=df.manufacturer.astype('category').cat.codes, alpha=.9, data=df, cmap=\"Set1\", edgecolors='black', linewidths=.5)\n",
    "\n",
    "# Add a graph in each part\n",
    "sns.boxplot(df.hwy, ax=ax_right, orient=\"v\")\n",
    "sns.boxplot(df.displ, ax=ax_bottom, orient=\"h\")\n",
    "\n",
    "# Decorations ------------------\n",
    "# Remove x axis name for the boxplot\n",
    "ax_bottom.set(xlabel='')\n",
    "ax_right.set(ylabel='')\n",
    "\n",
    "# Main Title, Xlabel and YLabel\n",
    "ax_main.set(title='Scatterplot with Histograms \\n displ vs hwy', xlabel='displ', ylabel='hwy')\n",
    "\n",
    "# Set font size of different components\n",
    "ax_main.title.set_fontsize(20)\n",
    "for item in ([ax_main.xaxis.label, ax_main.yaxis.label] + ax_main.get_xticklabels() + ax_main.get_yticklabels()):\n",
    "    item.set_fontsize(14)\n",
    "\n",
    "plt.show()\n",
    "\n",
    "相关图 （Correllogram）\n",
    "相关图用于直观地查看给定数据框（或二维数组）中所有可能的数值变量对之间的相关度量。\n",
    "\n",
    "# Import Dataset\n",
    "df = pd.read_csv(\"mtcars.csv\")\n",
    "\n",
    "# Plot\n",
    "plt.figure(figsize=(12,10), dpi= 80)\n",
    "sns.heatmap(df.corr(), xticklabels=df.corr().columns, yticklabels=df.corr().columns, cmap='RdYlGn', center=0, annot=True)\n",
    "\n",
    "# Decorations\n",
    "plt.title('Correlogram of mtcars', fontsize=22)\n",
    "plt.xticks(fontsize=12)\n",
    "plt.yticks(fontsize=12)\n",
    "plt.show()\n",
    "\n",
    "矩阵图 （Pairwise Plot）\n",
    "矩阵图是探索性分析中的最爱，用于理解所有可能的数值变量对之间的关系。 它是双变量分析的必备工具。\n",
    "\n",
    "# Load Dataset\n",
    "df = sns.load_dataset('iris')\n",
    "\n",
    "# Plot\n",
    "plt.figure(figsize=(10,8), dpi= 80)\n",
    "sns.pairplot(df, kind=\"scatter\", hue=\"species\", plot_kws=dict(s=80, edgecolor=\"white\", linewidth=2.5))\n",
    "plt.show()\n",
    "<Figure size 800x640 with 0 Axes>\n",
    "\n",
    "# Load Dataset\n",
    "df = sns.load_dataset('iris')\n",
    "\n",
    "# Plot\n",
    "plt.figure(figsize=(10,8), dpi= 80)\n",
    "sns.pairplot(df, kind=\"reg\", hue=\"species\")\n",
    "plt.show()\n",
    "<Figure size 800x640 with 0 Axes>\n",
    "\n",
    "2、偏差 （Deviation）\n",
    "发散型条形图 （Diverging Bars）\n",
    "如果您想根据单个指标查看项目的变化情况，并可视化此差异的顺序和数量，那么散型条形图 （Diverging Bars） 是一个很好的工具。 它有助于快速区分数据中组的性能，并且非常直观，并且可以立即传达这一点。\n",
    "\n",
    "# Prepare Data\n",
    "df = pd.read_csv(\"mtcars.csv\")\n",
    "x = df.loc[:, ['mpg']]\n",
    "df['mpg_z'] = (x - x.mean())/x.std()\n",
    "df['colors'] = ['red' if x < 0 else 'green' for x in df['mpg_z']]\n",
    "df.sort_values('mpg_z', inplace=True)\n",
    "df.reset_index(inplace=True)\n",
    "\n",
    "# Draw plot\n",
    "plt.figure(figsize=(14,10), dpi= 80)\n",
    "plt.hlines(y=df.index, xmin=0, xmax=df.mpg_z, color=df.colors, alpha=0.4, linewidth=5)\n",
    "\n",
    "# Decorations\n",
    "plt.gca().set(ylabel='$Model$', xlabel='$Mileage$')\n",
    "plt.yticks(df.index, df.cars, fontsize=12)\n",
    "plt.title('Diverging Bars of Car Mileage', fontdict={'size':20})\n",
    "plt.grid(linestyle='--', alpha=0.5)\n",
    "plt.show()\n",
    "\n",
    "发散型文本 （Diverging Texts）\n",
    "发散型文本 （Diverging Texts）与发散型条形图 （Diverging Bars）相似，如果你想以一种漂亮和可呈现的方式显示图表中每个项目的价值，就可以使用这种方法。\n",
    "\n",
    "# Prepare Data\n",
    "df = pd.read_csv(\"mtcars.csv\")\n",
    "x = df.loc[:, ['mpg']]\n",
    "df['mpg_z'] = (x - x.mean())/x.std()\n",
    "df['colors'] = ['red' if x < 0 else 'green' for x in df['mpg_z']]\n",
    "df.sort_values('mpg_z', inplace=True)\n",
    "df.reset_index(inplace=True)\n",
    "\n",
    "# Draw plot\n",
    "plt.figure(figsize=(14,14), dpi= 80)\n",
    "plt.hlines(y=df.index, xmin=0, xmax=df.mpg_z)\n",
    "for x, y, tex in zip(df.mpg_z, df.index, df.mpg_z):\n",
    "    t = plt.text(x, y, round(tex, 2), horizontalalignment='right' if x < 0 else 'left', \n",
    "                 verticalalignment='center', fontdict={'color':'red' if x < 0 else 'green', 'size':14})\n",
    "\n",
    "# Decorations    \n",
    "plt.yticks(df.index, df.cars, fontsize=12)\n",
    "plt.title('Diverging Text Bars of Car Mileage', fontdict={'size':20})\n",
    "plt.grid(linestyle='--', alpha=0.5)\n",
    "plt.xlim(-2.5, 2.5)\n",
    "plt.show()\n",
    "\n",
    "发散型包点图 （Diverging Dot Plot）\n",
    "发散型包点图 （Diverging Dot Plot）也类似于发散型条形图 （Diverging Bars）。 然而，与发散型条形图 （Diverging Bars）相比，条的缺失减少了组之间的对比度和差异。\n",
    "\n",
    "# Prepare Data\n",
    "df = pd.read_csv(\"mtcars.csv\")\n",
    "x = df.loc[:, ['mpg']]\n",
    "df['mpg_z'] = (x - x.mean())/x.std()\n",
    "df['colors'] = ['red' if x < 0 else 'darkgreen' for x in df['mpg_z']]\n",
    "df.sort_values('mpg_z', inplace=True)\n",
    "df.reset_index(inplace=True)\n",
    "\n",
    "# Draw plot\n",
    "plt.figure(figsize=(14,16), dpi= 80)\n",
    "plt.scatter(df.mpg_z, df.index, s=450, alpha=.6, color=df.colors)\n",
    "for x, y, tex in zip(df.mpg_z, df.index, df.mpg_z):\n",
    "    t = plt.text(x, y, round(tex, 1), horizontalalignment='center', \n",
    "                 verticalalignment='center', fontdict={'color':'white'})\n",
    "\n",
    "# Decorations\n",
    "# Lighten borders\n",
    "plt.gca().spines[\"top\"].set_alpha(.3)\n",
    "plt.gca().spines[\"bottom\"].set_alpha(.3)\n",
    "plt.gca().spines[\"right\"].set_alpha(.3)\n",
    "plt.gca().spines[\"left\"].set_alpha(.3)\n",
    "\n",
    "plt.yticks(df.index, df.cars)\n",
    "plt.title('Diverging Dotplot of Car Mileage', fontdict={'size':20})\n",
    "plt.xlabel('$Mileage$')\n",
    "plt.grid(linestyle='--', alpha=0.5)\n",
    "plt.xlim(-2.5, 2.5)\n",
    "plt.show()\n",
    "\n",
    "带标记的发散型棒棒糖图 （Diverging Lollipop Chart with Markers）\n",
    "带标记的棒棒糖图通过强调您想要引起注意的任何重要数据点并在图表中适当地给出推理，提供了一种对差异进行可视化的灵活方式。\n",
    "\n",
    "# Prepare Data\n",
    "df = pd.read_csv(\"mtcars.csv\")\n",
    "x = df.loc[:, ['mpg']]\n",
    "df['mpg_z'] = (x - x.mean())/x.std()\n",
    "df['colors'] = 'black'\n",
    "\n",
    "# color fiat differently\n",
    "df.loc[df.cars == 'Fiat X1-9', 'colors'] = 'darkorange'\n",
    "df.sort_values('mpg_z', inplace=True)\n",
    "df.reset_index(inplace=True)\n",
    "\n",
    "\n",
    "# Draw plot\n",
    "import matplotlib.patches as patches\n",
    "\n",
    "plt.figure(figsize=(14,16), dpi= 80)\n",
    "plt.hlines(y=df.index, xmin=0, xmax=df.mpg_z, color=df.colors, alpha=0.4, linewidth=1)\n",
    "plt.scatter(df.mpg_z, df.index, color=df.colors, s=[600 if x == 'Fiat X1-9' else 300 for x in df.cars], alpha=0.6)\n",
    "plt.yticks(df.index, df.cars)\n",
    "plt.xticks(fontsize=12)\n",
    "\n",
    "# Annotate\n",
    "plt.annotate('Mercedes Models', xy=(0.0, 11.0), xytext=(1.0, 11), xycoords='data', \n",
    "            fontsize=15, ha='center', va='center',\n",
    "            bbox=dict(boxstyle='square', fc='firebrick'),\n",
    "            arrowprops=dict(arrowstyle='-[, widthB=2.0, lengthB=1.5', lw=2.0, color='steelblue'), color='white')\n",
    "\n",
    "# Add Patches\n",
    "p1 = patches.Rectangle((-2.0, -1), width=.3, height=3, alpha=.2, facecolor='red')\n",
    "p2 = patches.Rectangle((1.5, 27), width=.8, height=5, alpha=.2, facecolor='green')\n",
    "plt.gca().add_patch(p1)\n",
    "plt.gca().add_patch(p2)\n",
    "\n",
    "# Decorate\n",
    "plt.title('Diverging Bars of Car Mileage', fontdict={'size':20})\n",
    "plt.grid(linestyle='--', alpha=0.5)\n",
    "plt.show()\n",
    "\n",
    "面积图 （Area Chart）\n",
    "通过对轴和线之间的区域进行着色，面积图不仅强调峰和谷，而且还强调高点和低点的持续时间。 高点持续时间越长，线下面积越大。\n",
    "这里annotate的函数值得学习，台风路径信息的框框或者文字避让算法，都需要用到这个函数。\n",
    "\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "\n",
    "# Prepare Data\n",
    "df = pd.read_csv(\"economics.csv\", parse_dates=['date']).head(100)\n",
    "x = np.arange(df.shape[0])\n",
    "y_returns = (df.psavert.diff().fillna(0)/df.psavert.shift(1)).fillna(0) * 100\n",
    "\n",
    "# Plot\n",
    "plt.figure(figsize=(16,10), dpi= 80)\n",
    "plt.fill_between(x[1:], y_returns[1:], 0, where=y_returns[1:] >= 0, facecolor='green', interpolate=True, alpha=0.7)\n",
    "plt.fill_between(x[1:], y_returns[1:], 0, where=y_returns[1:] <= 0, facecolor='red', interpolate=True, alpha=0.7)\n",
    "\n",
    "# Annotate\n",
    "plt.annotate('Peak \\n1975', xy=(94.0, 21.0), xytext=(88.0, 28),\n",
    "             bbox=dict(boxstyle='square', fc='firebrick'),\n",
    "             arrowprops=dict(facecolor='steelblue', shrink=0.05), fontsize=15, color='white')\n",
    "\n",
    "\n",
    "# Decorations\n",
    "xtickvals = [str(m)[:3].upper()+\"-\"+str(y) for y,m in zip(df.date.dt.year, df.date.dt.month_name())]\n",
    "plt.gca().set_xticks(x[::6])\n",
    "plt.gca().set_xticklabels(xtickvals[::6], rotation=90, fontdict={'horizontalalignment': 'center', 'verticalalignment': 'center_baseline'})\n",
    "plt.ylim(-35,35)\n",
    "plt.xlim(1,100)\n",
    "plt.title(\"Month Economics Return %\", fontsize=22)\n",
    "plt.ylabel('Monthly returns %')\n",
    "plt.grid(alpha=0.5)\n",
    "plt.show()\n",
    "\n",
    "3、排序 （Ranking）\n",
    "有序条形图 （Ordered Bar Chart）\n",
    "有序条形图有效地传达了项目的排名顺序。 但是，在图表上方添加度量标准的值，用户可以从图表本身获取精确信息。\n",
    "\n",
    "# Prepare Data\n",
    "df_raw = pd.read_csv(\"mpg_ggplot2.csv\")\n",
    "df = df_raw[['cty', 'manufacturer']].groupby('manufacturer').apply(lambda x: x.mean())\n",
    "df.sort_values('cty', inplace=True)\n",
    "df.reset_index(inplace=True)\n",
    "\n",
    "# Draw plot\n",
    "import matplotlib.patches as patches\n",
    "\n",
    "fig, ax = plt.subplots(figsize=(16,10), facecolor='white', dpi= 80)\n",
    "ax.vlines(x=df.index, ymin=0, ymax=df.cty, color='firebrick', alpha=0.7, linewidth=20)\n",
    "\n",
    "# Annotate Text\n",
    "for i, cty in enumerate(df.cty):\n",
    "    ax.text(i, cty+0.5, round(cty, 1), horizontalalignment='center')\n",
    "\n",
    "\n",
    "# Title, Label, Ticks and Ylim\n",
    "ax.set_title('Bar Chart for Highway Mileage', fontdict={'size':22})\n",
    "ax.set(ylabel='Miles Per Gallon', ylim=(0, 30))\n",
    "plt.xticks(df.index, df.manufacturer.str.upper(), rotation=60, horizontalalignment='right', fontsize=12)\n",
    "\n",
    "# Add patches to color the X axis labels\n",
    "p1 = patches.Rectangle((.57, -0.005), width=.33, height=.13, alpha=.1, facecolor='green', transform=fig.transFigure)\n",
    "p2 = patches.Rectangle((.124, -0.005), width=.446, height=.13, alpha=.1, facecolor='red', transform=fig.transFigure)\n",
    "fig.add_artist(p1)\n",
    "fig.add_artist(p2)\n",
    "plt.show()\n",
    "\n",
    "棒棒糖图 （Lollipop Chart）\n",
    "棒棒糖图表以一种视觉上令人愉悦的方式提供与有序条形图类似的目的。\n",
    "\n",
    "# Prepare Data\n",
    "df_raw = pd.read_csv(\"mpg_ggplot2.csv\")\n",
    "df = df_raw[['cty', 'manufacturer']].groupby('manufacturer').apply(lambda x: x.mean())\n",
    "df.sort_values('cty', inplace=True)\n",
    "df.reset_index(inplace=True)\n",
    "\n",
    "# Draw plot\n",
    "fig, ax = plt.subplots(figsize=(16,10), dpi= 80)\n",
    "ax.vlines(x=df.index, ymin=0, ymax=df.cty, color='firebrick', alpha=0.7, linewidth=2)\n",
    "ax.scatter(x=df.index, y=df.cty, s=75, color='firebrick', alpha=0.7)\n",
    "\n",
    "# Title, Label, Ticks and Ylim\n",
    "ax.set_title('Lollipop Chart for Highway Mileage', fontdict={'size':22})\n",
    "ax.set_ylabel('Miles Per Gallon')\n",
    "ax.set_xticks(df.index)\n",
    "ax.set_xticklabels(df.manufacturer.str.upper(), rotation=60, fontdict={'horizontalalignment': 'right', 'size':12})\n",
    "ax.set_ylim(0, 30)\n",
    "\n",
    "# Annotate\n",
    "for row in df.itertuples():\n",
    "    ax.text(row.Index, row.cty+.5, s=round(row.cty, 2), horizontalalignment= 'center', verticalalignment='bottom', fontsize=14)\n",
    "\n",
    "plt.show()\n",
    "\n",
    "包点图 （Dot Plot）\n",
    "包点图表传达了项目的排名顺序，并且由于它沿水平轴对齐，因此您可以更容易地看到点彼此之间的距离。\n",
    "\n",
    "# Prepare Data\n",
    "df_raw = pd.read_csv(\"mpg_ggplot2.csv\")\n",
    "df = df_raw[['cty', 'manufacturer']].groupby('manufacturer').apply(lambda x: x.mean())\n",
    "df.sort_values('cty', inplace=True)\n",
    "df.reset_index(inplace=True)\n",
    "\n",
    "# Draw plot\n",
    "fig, ax = plt.subplots(figsize=(16,10), dpi= 80)\n",
    "ax.hlines(y=df.index, xmin=11, xmax=26, color='gray', alpha=0.7, linewidth=1, linestyles='dashdot')\n",
    "ax.scatter(y=df.index, x=df.cty, s=75, color='firebrick', alpha=0.7)\n",
    "\n",
    "# Title, Label, Ticks and Ylim\n",
    "ax.set_title('Dot Plot for Highway Mileage', fontdict={'size':22})\n",
    "ax.set_xlabel('Miles Per Gallon')\n",
    "ax.set_yticks(df.index)\n",
    "ax.set_yticklabels(df.manufacturer.str.title(), fontdict={'horizontalalignment': 'right'})\n",
    "ax.set_xlim(10, 27)\n",
    "plt.show()\n",
    "\n",
    "坡度图 （Slope Chart）\n",
    "坡度图最适合比较给定人/项目的“前”和“后”位置。\n",
    "\n",
    "import matplotlib.lines as mlines\n",
    "# Import Data\n",
    "df = pd.read_csv(\"gdppercap.csv\")\n",
    "\n",
    "left_label = [str(c) + ', '+ str(round(y)) for c, y in zip(df.continent, df['1952'])]\n",
    "right_label = [str(c) + ', '+ str(round(y)) for c, y in zip(df.continent, df['1957'])]\n",
    "klass = ['red' if (y1-y2) < 0 else 'green' for y1, y2 in zip(df['1952'], df['1957'])]\n",
    "\n",
    "# draw line\n",
    "# https://stackoverflow.com/questions/36470343/how-to-draw-a-line-with-matplotlib/36479941\n",
    "def newline(p1, p2, color='black'):\n",
    "    ax = plt.gca()\n",
    "    l = mlines.Line2D([p1[0],p2[0]], [p1[1],p2[1]], color='red' if p1[1]-p2[1] > 0 else 'green', marker='o', markersize=6)\n",
    "    ax.add_line(l)\n",
    "    return l\n",
    "\n",
    "fig, ax = plt.subplots(1,1,figsize=(14,14), dpi= 80)\n",
    "\n",
    "# Vertical Lines\n",
    "ax.vlines(x=1, ymin=500, ymax=13000, color='black', alpha=0.7, linewidth=1, linestyles='dotted')\n",
    "ax.vlines(x=3, ymin=500, ymax=13000, color='black', alpha=0.7, linewidth=1, linestyles='dotted')\n",
    "\n",
    "# Points\n",
    "ax.scatter(y=df['1952'], x=np.repeat(1, df.shape[0]), s=10, color='black', alpha=0.7)\n",
    "ax.scatter(y=df['1957'], x=np.repeat(3, df.shape[0]), s=10, color='black', alpha=0.7)\n",
    "\n",
    "# Line Segmentsand Annotation\n",
    "for p1, p2, c in zip(df['1952'], df['1957'], df['continent']):\n",
    "    newline([1,p1], [3,p2])\n",
    "    ax.text(1-0.05, p1, c + ', ' + str(round(p1)), horizontalalignment='right', verticalalignment='center', fontdict={'size':14})\n",
    "    ax.text(3+0.05, p2, c + ', ' + str(round(p2)), horizontalalignment='left', verticalalignment='center', fontdict={'size':14})\n",
    "\n",
    "# 'Before' and 'After' Annotations\n",
    "ax.text(1-0.05, 13000, 'BEFORE', horizontalalignment='right', verticalalignment='center', fontdict={'size':18, 'weight':700})\n",
    "ax.text(3+0.05, 13000, 'AFTER', horizontalalignment='left', verticalalignment='center', fontdict={'size':18, 'weight':700})\n",
    "\n",
    "# Decoration\n",
    "ax.set_title(\"Slopechart: Comparing GDP Per Capita between 1952 vs 1957\", fontdict={'size':22})\n",
    "ax.set(xlim=(0,4), ylim=(0,14000), ylabel='Mean GDP Per Capita')\n",
    "ax.set_xticks([1,3])\n",
    "ax.set_xticklabels([\"1952\", \"1957\"])\n",
    "plt.yticks(np.arange(500, 13000, 2000), fontsize=12)\n",
    "\n",
    "# Lighten borders\n",
    "plt.gca().spines[\"top\"].set_alpha(.0)\n",
    "plt.gca().spines[\"bottom\"].set_alpha(.0)\n",
    "plt.gca().spines[\"right\"].set_alpha(.0)\n",
    "plt.gca().spines[\"left\"].set_alpha(.0)\n",
    "plt.show()\n",
    "\n",
    "哑铃图 （Dumbbell Plot）\n",
    "哑铃图表传达了各种项目的“前”和“后”位置以及项目的等级排序。 如果您想要将特定项目/计划对不同对象的影响可视化，那么它非常有用。\n",
    "\n",
    "import matplotlib.lines as mlines\n",
    "\n",
    "# Import Data\n",
    "df = pd.read_csv(\"health.csv\")\n",
    "df.sort_values('pct_2014', inplace=True)\n",
    "df.reset_index(inplace=True)\n",
    "\n",
    "# Func to draw line segment\n",
    "def newline(p1, p2, color='black'):\n",
    "    ax = plt.gca()\n",
    "    l = mlines.Line2D([p1[0],p2[0]], [p1[1],p2[1]], color='skyblue')\n",
    "    ax.add_line(l)\n",
    "    return l\n",
    "\n",
    "# Figure and Axes\n",
    "fig, ax = plt.subplots(1,1,figsize=(14,14), facecolor='#f7f7f7', dpi= 80)\n",
    "\n",
    "# Vertical Lines\n",
    "ax.vlines(x=.05, ymin=0, ymax=26, color='black', alpha=1, linewidth=1, linestyles='dotted')\n",
    "ax.vlines(x=.10, ymin=0, ymax=26, color='black', alpha=1, linewidth=1, linestyles='dotted')\n",
    "ax.vlines(x=.15, ymin=0, ymax=26, color='black', alpha=1, linewidth=1, linestyles='dotted')\n",
    "ax.vlines(x=.20, ymin=0, ymax=26, color='black', alpha=1, linewidth=1, linestyles='dotted')\n",
    "\n",
    "# Points\n",
    "ax.scatter(y=df['index'], x=df['pct_2013'], s=50, color='#0e668b', alpha=0.7)\n",
    "ax.scatter(y=df['index'], x=df['pct_2014'], s=50, color='#a3c4dc', alpha=0.7)\n",
    "\n",
    "# Line Segments\n",
    "for i, p1, p2 in zip(df['index'], df['pct_2013'], df['pct_2014']):\n",
    "    newline([p1, i], [p2, i])\n",
    "\n",
    "# Decoration\n",
    "ax.set_facecolor('#f7f7f7')\n",
    "ax.set_title(\"Dumbell Chart: Pct Change - 2013 vs 2014\", fontdict={'size':22})\n",
    "ax.set(xlim=(0,.25), ylim=(-1, 27), ylabel='Mean GDP Per Capita')\n",
    "ax.set_xticks([.05, .1, .15, .20])\n",
    "ax.set_xticklabels(['5%', '15%', '20%', '25%'])\n",
    "ax.set_xticklabels(['5%', '15%', '20%', '25%'])    \n",
    "plt.show()\n",
    "\n",
    "4、分布 （Distribution）\n",
    "连续变量的直方图 （Histogram for Continuous Variable）\n",
    "直方图显示给定变量的频率分布。 下面的图表示基于类型变量对频率条进行分组，从而更好地了解连续变量和类型变量。\n",
    "也可以看成堆叠图的形式，同样适用于空气质量的分级。\n",
    "\n",
    "# Import Data\n",
    "df = pd.read_csv(\"mpg_ggplot2.csv\")\n",
    "\n",
    "# Prepare data\n",
    "x_var = 'displ'\n",
    "groupby_var = 'class'\n",
    "df_agg = df.loc[:, [x_var, groupby_var]].groupby(groupby_var)\n",
    "vals = [df[x_var].values.tolist() for i, df in df_agg]\n",
    "\n",
    "# Draw\n",
    "plt.figure(figsize=(16,9), dpi= 80)\n",
    "colors = [plt.cm.Spectral(i/float(len(vals)-1)) for i in range(len(vals))]\n",
    "n, bins, patches = plt.hist(vals, 30, stacked=True, density=False, color=colors[:len(vals)])\n",
    "\n",
    "# Decoration\n",
    "plt.legend({group:col for group, col in zip(np.unique(df[groupby_var]).tolist(), colors[:len(vals)])})\n",
    "plt.title(f\"Stacked Histogram of ${x_var}$ colored by ${groupby_var}$\", fontsize=22)\n",
    "plt.xlabel(x_var)\n",
    "plt.ylabel(\"Frequency\")\n",
    "plt.ylim(0, 25)\n",
    "plt.xticks(ticks=bins[::3], labels=[round(b,1) for b in bins[::3]])\n",
    "plt.show()\n",
    "\n",
    "类型变量的直方图 （Histogram for Categorical Variable）\n",
    "类型变量的直方图显示该变量的频率分布。 通过对条形图进行着色，可以将分布与表示颜色的另一个类型变量相关联。\n",
    "\n",
    "# Import Data\n",
    "df = pd.read_csv(\"mpg_ggplot2.csv\")\n",
    "\n",
    "# Prepare data\n",
    "x_var = 'manufacturer'\n",
    "groupby_var = 'class'\n",
    "df_agg = df.loc[:, [x_var, groupby_var]].groupby(groupby_var)\n",
    "vals = [df[x_var].values.tolist() for i, df in df_agg]\n",
    "\n",
    "# Draw\n",
    "plt.figure(figsize=(16,9), dpi= 80)\n",
    "colors = [plt.cm.Spectral(i/float(len(vals)-1)) for i in range(len(vals))]\n",
    "n, bins, patches = plt.hist(vals, df[x_var].unique().__len__(), stacked=True, density=False, color=colors[:len(vals)])\n",
    "\n",
    "# Decoration\n",
    "plt.legend({group:col for group, col in zip(np.unique(df[groupby_var]).tolist(), colors[:len(vals)])})\n",
    "plt.title(f\"Stacked Histogram of ${x_var}$ colored by ${groupby_var}$\", fontsize=22)\n",
    "plt.xlabel(x_var)\n",
    "plt.ylabel(\"Frequency\")\n",
    "plt.ylim(0, 40)\n",
    "plt.xticks(rotation=90, horizontalalignment='left')\n",
    "plt.show()\n",
    "\n",
    "密度图 （Density Plot）\n",
    "密度图是一种常用工具，用于可视化连续变量的分布。 通过“响应”变量对它们进行分组，您可以检查 X 和 Y 之间的关系。以下情况用于表示目的，以描述城市里程的分布如何随着汽缸数的变化而变化。\n",
    "\n",
    "# Import Data\n",
    "df = pd.read_csv(\"mpg_ggplot2.csv\")\n",
    "\n",
    "# Draw Plot\n",
    "plt.figure(figsize=(16,10), dpi= 80)\n",
    "sns.kdeplot(df.loc[df['cyl'] == 4, \"cty\"], shade=True, color=\"g\", label=\"Cyl=4\", alpha=.7)\n",
    "sns.kdeplot(df.loc[df['cyl'] == 5, \"cty\"], shade=True, color=\"deeppink\", label=\"Cyl=5\", alpha=.7)\n",
    "sns.kdeplot(df.loc[df['cyl'] == 6, \"cty\"], shade=True, color=\"dodgerblue\", label=\"Cyl=6\", alpha=.7)\n",
    "sns.kdeplot(df.loc[df['cyl'] == 8, \"cty\"], shade=True, color=\"orange\", label=\"Cyl=8\", alpha=.7)\n",
    "\n",
    "# Decoration\n",
    "plt.title('Density Plot of City Mileage by n_Cylinders', fontsize=22)\n",
    "plt.legend()\n",
    "plt.show()\n",
    "\n",
    "直方密度线图 （Density Curves with Histogram）\n",
    "带有直方图的密度曲线汇集了两个图所传达的集体信息，因此您可以将它们放在一个图中而不是两个图中。\n",
    "\n",
    "# Import Data\n",
    "df = pd.read_csv(\"mpg_ggplot2.csv\")\n",
    "\n",
    "# Draw Plot\n",
    "plt.figure(figsize=(13,10), dpi= 80)\n",
    "sns.distplot(df.loc[df['class'] == 'compact', \"cty\"], color=\"dodgerblue\", label=\"Compact\", hist_kws={'alpha':.7}, kde_kws={'linewidth':3})\n",
    "sns.distplot(df.loc[df['class'] == 'suv', \"cty\"], color=\"orange\", label=\"SUV\", hist_kws={'alpha':.7}, kde_kws={'linewidth':3})\n",
    "sns.distplot(df.loc[df['class'] == 'minivan', \"cty\"], color=\"g\", label=\"minivan\", hist_kws={'alpha':.7}, kde_kws={'linewidth':3})\n",
    "plt.ylim(0, 0.35)\n",
    "\n",
    "# Decoration\n",
    "plt.title('Density Plot of City Mileage by Vehicle Type', fontsize=22)\n",
    "plt.legend()\n",
    "plt.show()\n",
    "\n",
    "Joy Plot\n",
    "Joy Plot允许不同组的密度曲线重叠，这是一种可视化大量分组数据的彼此关系分布的好方法。 它看起来很悦目，并清楚地传达了正确的信息。 它可以使用基于 matplotlib 的 joypy 包轻松构建。 （需要安装 joypy 库）\n",
    "\n",
    "import joypy\n",
    "\n",
    "# Import Data\n",
    "mpg = pd.read_csv(\"mpg_ggplot2.csv\")\n",
    "\n",
    "# Draw Plot\n",
    "plt.figure(figsize=(16,10), dpi= 80)\n",
    "fig, axes = joypy.joyplot(mpg, column=['hwy', 'cty'], by=\"class\", ylim='own', figsize=(14,10))\n",
    "\n",
    "# Decoration\n",
    "plt.title('Joy Plot of City and Highway Mileage by Class', fontsize=22)\n",
    "plt.show()\n",
    "<Figure size 1280x800 with 0 Axes>\n",
    "\n",
    "分布式包点图 （Distributed Dot Plot）\n",
    "分布式包点图显示按组分割的点的单变量分布。 点数越暗，该区域的数据点集中度越高。 通过对中位数进行不同着色，组的真实定位立即变得明显。\n",
    "\n",
    "import matplotlib.patches as mpatches\n",
    "\n",
    "# Prepare Data\n",
    "df_raw = pd.read_csv(\"mpg_ggplot2.csv\")\n",
    "cyl_colors = {4:'tab:red', 5:'tab:green', 6:'tab:blue', 8:'tab:orange'}\n",
    "df_raw['cyl_color'] = df_raw.cyl.map(cyl_colors)\n",
    "\n",
    "# Mean and Median city mileage by make\n",
    "df = df_raw[['cty', 'manufacturer']].groupby('manufacturer').apply(lambda x: x.mean())\n",
    "df.sort_values('cty', ascending=False, inplace=True)\n",
    "df.reset_index(inplace=True)\n",
    "df_median = df_raw[['cty', 'manufacturer']].groupby('manufacturer').apply(lambda x: x.median())\n",
    "\n",
    "# Draw horizontal lines\n",
    "fig, ax = plt.subplots(figsize=(16,10), dpi= 80)\n",
    "ax.hlines(y=df.index, xmin=0, xmax=40, color='gray', alpha=0.5, linewidth=.5, linestyles='dashdot')\n",
    "\n",
    "# Draw the Dots\n",
    "for i, make in enumerate(df.manufacturer):\n",
    "    df_make = df_raw.loc[df_raw.manufacturer==make, :]\n",
    "    #ax.scatter(y=np.repeat(i, df_make.shape[0]), x='cty', data=df_make, s=75, edgecolors='gray', c='w', alpha=0.5)\n",
    "    ax.scatter(y=i, x='cty', data=df_median.loc[df_median.index==make, :], s=75, c='firebrick')\n",
    "\n",
    "# Annotate    \n",
    "ax.text(33, 13, \"$red \\; dots \\; are \\; the \\: median$\", fontdict={'size':12}, color='firebrick')\n",
    "\n",
    "# Decorations\n",
    "red_patch = plt.plot([],[], marker=\"o\", ms=10, ls=\"\", mec=None, color='firebrick', label=\"Median\")\n",
    "plt.legend(handles=red_patch)\n",
    "ax.set_title('Distribution of City Mileage by Make', fontdict={'size':22})\n",
    "ax.set_xlabel('Miles Per Gallon (City)', alpha=0.7)\n",
    "ax.set_yticks(df.index)\n",
    "ax.set_yticklabels(df.manufacturer.str.title(), fontdict={'horizontalalignment': 'right'}, alpha=0.7)\n",
    "ax.set_xlim(1, 40)\n",
    "plt.xticks(alpha=0.7)\n",
    "plt.gca().spines[\"top\"].set_visible(False)    \n",
    "plt.gca().spines[\"bottom\"].set_visible(False)    \n",
    "plt.gca().spines[\"right\"].set_visible(False)    \n",
    "plt.gca().spines[\"left\"].set_visible(False)   \n",
    "plt.grid(axis='both', alpha=.4, linewidth=.1)\n",
    "plt.show()\n",
    "\n",
    "箱形图 （Box Plot）\n",
    "箱形图是一种可视化分布的好方法，记住中位数、第25个第45个四分位数和异常值。 但是，您需要注意解释可能会扭曲该组中包含的点数的框的大小。 因此，手动提供每个框中的观察数量可以帮助克服这个缺点。\n",
    "\n",
    "例如，左边的前两个框具有相同大小的框，即使它们的值分别是5和47。 因此，写入该组中的观察数量是必要的。\n",
    "\n",
    "# Import Data\n",
    "df = pd.read_csv(\"mpg_ggplot2.csv\")\n",
    "\n",
    "# Draw Plot\n",
    "plt.figure(figsize=(13,10), dpi= 80)\n",
    "sns.boxplot(x='class', y='hwy', data=df, notch=False)\n",
    "\n",
    "# Add N Obs inside boxplot (optional)\n",
    "def add_n_obs(df,group_col,y):\n",
    "    medians_dict = {grp[0]:grp[1][y].median() for grp in df.groupby(group_col)}\n",
    "    xticklabels = [x.get_text() for x in plt.gca().get_xticklabels()]\n",
    "    n_obs = df.groupby(group_col)[y].size().values\n",
    "    for (x, xticklabel), n_ob in zip(enumerate(xticklabels), n_obs):\n",
    "        plt.text(x, medians_dict[xticklabel]*1.01, \"#obs : \"+str(n_ob), horizontalalignment='center', fontdict={'size':14}, color='white')\n",
    "\n",
    "add_n_obs(df,group_col='class',y='hwy')    \n",
    "\n",
    "# Decoration\n",
    "plt.title('Box Plot of Highway Mileage by Vehicle Class', fontsize=22)\n",
    "plt.ylim(10, 40)\n",
    "plt.show()\n",
    "\n",
    "包点+箱形图 （Dot + Box Plot）\n",
    "包点+箱形图 （Dot + Box Plot）传达类似于分组的箱形图信息。 此外，这些点可以了解每组中有多少数据点。\n",
    "\n",
    "# Import Data\n",
    "df = pd.read_csv(\"mpg_ggplot2.csv\")\n",
    "\n",
    "# Draw Plot\n",
    "plt.figure(figsize=(13,10), dpi= 80)\n",
    "sns.boxplot(x='class', y='hwy', data=df, hue='cyl')\n",
    "sns.stripplot(x='class', y='hwy', data=df, color='black', size=3, jitter=1)\n",
    "\n",
    "for i in range(len(df['class'].unique())-1):\n",
    "    plt.vlines(i+.5, 10, 45, linestyles='solid', colors='gray', alpha=0.2)\n",
    "\n",
    "# Decoration\n",
    "plt.title('Box Plot of Highway Mileage by Vehicle Class', fontsize=22)\n",
    "plt.legend(title='Cylinders')\n",
    "plt.show()\n",
    "\n",
    "小提琴图 （Violin Plot）\n",
    "小提琴图是箱形图在视觉上令人愉悦的替代品。 小提琴的形状或面积取决于它所持有的观察次数。 但是，小提琴图可能更难以阅读，并且在专业设置中不常用。\n",
    "\n",
    "# Import Data\n",
    "df = pd.read_csv(\"mpg_ggplot2.csv\")\n",
    "\n",
    "# Draw Plot\n",
    "plt.figure(figsize=(13,10), dpi= 80)\n",
    "sns.violinplot(x='class', y='hwy', data=df, scale='width', inner='quartile')\n",
    "\n",
    "# Decoration\n",
    "plt.title('Violin Plot of Highway Mileage by Vehicle Class', fontsize=22)\n",
    "plt.show()\n",
    "\n",
    "人口金字塔 （Population Pyramid）\n",
    "人口金字塔可用于显示由数量排序的组的分布。 或者它也可以用于显示人口的逐级过滤，因为它在下面用于显示有多少人通过营销渠道的每个阶段。\n",
    "\n",
    "# Read data\n",
    "df = pd.read_csv(\"email_campaign_funnel.csv\")\n",
    "\n",
    "# Draw Plot\n",
    "plt.figure(figsize=(13,10), dpi= 80)\n",
    "group_col = 'Gender'\n",
    "order_of_bars = df.Stage.unique()[::-1]\n",
    "colors = [plt.cm.Spectral(i/float(len(df[group_col].unique())-1)) for i in range(len(df[group_col].unique()))]\n",
    "\n",
    "for c, group in zip(colors, df[group_col].unique()):\n",
    "    sns.barplot(x='Users', y='Stage', data=df.loc[df[group_col]==group, :], order=order_of_bars, color=c, label=group)\n",
    "\n",
    "# Decorations    \n",
    "plt.xlabel(\"$Users$\")\n",
    "plt.ylabel(\"Stage of Purchase\")\n",
    "plt.yticks(fontsize=12)\n",
    "plt.title(\"Population Pyramid of the Marketing Funnel\", fontsize=22)\n",
    "plt.legend()\n",
    "plt.show()\n",
    "\n",
    "分类图 （Categorical Plots）\n",
    "由 seaborn库 提供的分类图可用于可视化彼此相关的2个或更多分类变量的计数分布。\n",
    "\n",
    "# Load Dataset\n",
    "titanic = sns.load_dataset(\"titanic\")\n",
    "\n",
    "# Plot\n",
    "g = sns.catplot(\"alive\", col=\"deck\", col_wrap=4,\n",
    "                data=titanic[titanic.deck.notnull()],\n",
    "                kind=\"count\", height=3.5, aspect=.8, \n",
    "                palette='tab20')\n",
    "\n",
    "fig.suptitle('sf')\n",
    "plt.show()\n",
    "\n",
    "# Load Dataset\n",
    "titanic = sns.load_dataset(\"titanic\")\n",
    "\n",
    "# Plot\n",
    "sns.catplot(x=\"age\", y=\"embark_town\",\n",
    "            hue=\"sex\", col=\"class\",\n",
    "            data=titanic[titanic.embark_town.notnull()],\n",
    "            orient=\"h\", height=5, aspect=1, palette=\"tab10\",\n",
    "            kind=\"violin\", dodge=True, cut=0, bw=.2)\n",
    "<seaborn.axisgrid.FacetGrid at 0x22d0e6bba58>\n",
    "\n",
    "5、组成 （Composition）\n",
    "华夫饼图 （Waffle Chart）\n",
    "可以使用 pywaffle包 创建华夫饼图，并用于显示更大群体中的组的组成。（需要安装 pywaffle 库）\n",
    "\n",
    "# Reference: https://stackoverflow.com/questions/41400136/how-to-do-waffle-charts-in-python-square-piechart\n",
    "from pywaffle import Waffle\n",
    "\n",
    "# Import\n",
    "df_raw = pd.read_csv(\"mpg_ggplot2.csv\")\n",
    "\n",
    "# Prepare Data\n",
    "df = df_raw.groupby('class').size().reset_index(name='counts')\n",
    "n_categories = df.shape[0]\n",
    "colors = [plt.cm.inferno_r(i/float(n_categories)) for i in range(n_categories)]\n",
    "\n",
    "# Draw Plot and Decorate\n",
    "fig = plt.figure(\n",
    "    FigureClass=Waffle,\n",
    "    plots={\n",
    "        '111': {\n",
    "            'values': df['counts'],\n",
    "            'labels': [\"{0} ({1})\".format(n[0], n[1]) for n in df[['class', 'counts']].itertuples()],\n",
    "            'legend': {'loc': 'upper left', 'bbox_to_anchor': (1.05, 1), 'fontsize': 12},\n",
    "            'title': {'label': '# Vehicles by Class', 'loc': 'center', 'fontsize':18}\n",
    "        },\n",
    "    },\n",
    "    rows=7,\n",
    "    colors=colors,\n",
    "    figsize=(16, 9)\n",
    ")\n",
    "\n",
    "#! pip install pywaffle\n",
    "from pywaffle import Waffle\n",
    "\n",
    "# Import\n",
    "# df_raw = pd.read_csv(\"https://github.com/selva86/datasets/raw/master/mpg_ggplot2.csv\")\n",
    "\n",
    "# Prepare Data\n",
    "# By Class Data\n",
    "df_class = df_raw.groupby('class').size().reset_index(name='counts_class')\n",
    "n_categories = df_class.shape[0]\n",
    "colors_class = [plt.cm.Set3(i/float(n_categories)) for i in range(n_categories)]\n",
    "\n",
    "# By Cylinders Data\n",
    "df_cyl = df_raw.groupby('cyl').size().reset_index(name='counts_cyl')\n",
    "n_categories = df_cyl.shape[0]\n",
    "colors_cyl = [plt.cm.Spectral(i/float(n_categories)) for i in range(n_categories)]\n",
    "\n",
    "# By Make Data\n",
    "df_make = df_raw.groupby('manufacturer').size().reset_index(name='counts_make')\n",
    "n_categories = df_make.shape[0]\n",
    "colors_make = [plt.cm.tab20b(i/float(n_categories)) for i in range(n_categories)]\n",
    "\n",
    "\n",
    "# Draw Plot and Decorate\n",
    "fig = plt.figure(\n",
    "    FigureClass=Waffle,\n",
    "    plots={\n",
    "        '311': {\n",
    "            'values': df_class['counts_class'],\n",
    "            'labels': [\"{1}\".format(n[0], n[1]) for n in df_class[['class', 'counts_class']].itertuples()],\n",
    "            'legend': {'loc': 'upper left', 'bbox_to_anchor': (1.05, 1), 'fontsize': 12, 'title':'Class'},\n",
    "            'title': {'label': '# Vehicles by Class', 'loc': 'center', 'fontsize':18},\n",
    "            'colors': colors_class\n",
    "        },\n",
    "        '312': {\n",
    "            'values': df_cyl['counts_cyl'],\n",
    "            'labels': [\"{1}\".format(n[0], n[1]) for n in df_cyl[['cyl', 'counts_cyl']].itertuples()],\n",
    "            'legend': {'loc': 'upper left', 'bbox_to_anchor': (1.05, 1), 'fontsize': 12, 'title':'Cyl'},\n",
    "            'title': {'label': '# Vehicles by Cyl', 'loc': 'center', 'fontsize':18},\n",
    "            'colors': colors_cyl\n",
    "        },\n",
    "        '313': {\n",
    "            'values': df_make['counts_make'],\n",
    "            'labels': [\"{1}\".format(n[0], n[1]) for n in df_make[['manufacturer', 'counts_make']].itertuples()],\n",
    "            'legend': {'loc': 'upper left', 'bbox_to_anchor': (1.05, 1), 'fontsize': 12, 'title':'Manufacturer'},\n",
    "            'title': {'label': '# Vehicles by Make', 'loc': 'center', 'fontsize':18},\n",
    "            'colors': colors_make\n",
    "        }\n",
    "    },\n",
    "    rows=9,\n",
    "    figsize=(16, 14)\n",
    ")\n",
    "\n",
    "饼图 （Pie Chart）\n",
    "饼图是显示组成的经典方式。 然而，现在通常不建议使用它，因为馅饼部分的面积有时会变得误导。 因此，如果您要使用饼图，强烈建议明确记下饼图每个部分的百分比或数字。\n",
    "\n",
    "# Import\n",
    "df_raw = pd.read_csv(\"mpg_ggplot2.csv\")\n",
    "\n",
    "# Prepare Data\n",
    "df = df_raw.groupby('class').size()\n",
    "\n",
    "# Make the plot with pandas\n",
    "df.plot(kind='pie', subplots=True, figsize=(8, 8))\n",
    "plt.title(\"Pie Chart of Vehicle Class - Bad\")\n",
    "plt.ylabel(\"\")\n",
    "plt.show()\n",
    "\n",
    "# Import\n",
    "df_raw = pd.read_csv(\"mpg_ggplot2.csv\")\n",
    "\n",
    "# Prepare Data\n",
    "df = df_raw.groupby('class').size().reset_index(name='counts')\n",
    "\n",
    "# Draw Plot\n",
    "fig, ax = plt.subplots(figsize=(12, 7), subplot_kw=dict(aspect=\"equal\"), dpi= 80)\n",
    "\n",
    "data = df['counts']\n",
    "categories = df['class']\n",
    "explode = [0,0,0,0,0,0.1,0]\n",
    "\n",
    "def func(pct, allvals):\n",
    "    absolute = int(pct/100.*np.sum(allvals))\n",
    "    return \"{:.1f}% ({:d} )\".format(pct, absolute)\n",
    "\n",
    "wedges, texts, autotexts = ax.pie(data, \n",
    "                                  autopct=lambda pct: func(pct, data),\n",
    "                                  textprops=dict(color=\"w\"), \n",
    "                                  colors=plt.cm.Dark2.colors,\n",
    "                                 startangle=140,\n",
    "                                 explode=explode)\n",
    "\n",
    "# Decoration\n",
    "ax.legend(wedges, categories, title=\"Vehicle Class\", loc=\"center left\", bbox_to_anchor=(1, 0, 0.5, 1))\n",
    "plt.setp(autotexts, size=10, weight=700)\n",
    "ax.set_title(\"Class of Vehicles: Pie Chart\")\n",
    "plt.show()\n",
    "\n",
    "树形图 （Treemap）\n",
    "树形图类似于饼图，它可以更好地完成工作而不会误导每个组的贡献。（需要安装 squarify 库）\n",
    "\n",
    "import squarify \n",
    "\n",
    "# Import Data\n",
    "df_raw = pd.read_csv(\"mpg_ggplot2.csv\")\n",
    "\n",
    "# Prepare Data\n",
    "df = df_raw.groupby('class').size().reset_index(name='counts')\n",
    "labels = df.apply(lambda x: str(x[0]) + \"\\n (\" + str(x[1]) + \")\", axis=1)\n",
    "sizes = df['counts'].values.tolist()\n",
    "colors = [plt.cm.Spectral(i/float(len(labels))) for i in range(len(labels))]\n",
    "\n",
    "# Draw Plot\n",
    "plt.figure(figsize=(12,8), dpi= 80)\n",
    "squarify.plot(sizes=sizes, label=labels, color=colors, alpha=.8)\n",
    "\n",
    "# Decorate\n",
    "plt.title('Treemap of Vechile Class')\n",
    "plt.axis('off')\n",
    "plt.show()\n",
    "\n",
    "条形图 （Bar Chart）\n",
    "条形图是基于计数或任何给定指标可视化项目的经典方式。 在下面的图表中，我为每个项目使用了不同的颜色，但您通常可能希望为所有项目选择一种颜色，除非您按组对其进行着色。 颜色名称存储在下面代码中的all_colors中。 您可以通过在plt.plot()中设置颜色参数来更改条的颜色。\n",
    "\n",
    "import random\n",
    "\n",
    "# Import Data\n",
    "df_raw = pd.read_csv(\"mpg_ggplot2.csv\")\n",
    "\n",
    "# Prepare Data\n",
    "df = df_raw.groupby('manufacturer').size().reset_index(name='counts')\n",
    "n = df['manufacturer'].unique().__len__()+1\n",
    "all_colors = list(plt.cm.colors.cnames.keys())\n",
    "random.seed(100)\n",
    "c = random.choices(all_colors, k=n)\n",
    "\n",
    "# Plot Bars\n",
    "plt.figure(figsize=(16,10), dpi= 80)\n",
    "plt.bar(df['manufacturer'], df['counts'], color=c, width=.5)\n",
    "for i, val in enumerate(df['counts'].values):\n",
    "    plt.text(i, val, float(val), horizontalalignment='center', verticalalignment='bottom', fontdict={'fontweight':500, 'size':12})\n",
    "\n",
    "# Decoration\n",
    "plt.gca().set_xticklabels(df['manufacturer'], rotation=60, horizontalalignment= 'right')\n",
    "plt.title(\"Number of Vehicles by Manaufacturers\", fontsize=22)\n",
    "plt.ylabel('# Vehicles')\n",
    "plt.ylim(0, 45)\n",
    "plt.show()\n",
    "\n",
    "6、变化 （Change）\n",
    "时间序列图 （Time Series Plot）\n",
    "时间序列图用于显示给定度量随时间变化的方式。 在这里，您可以看到 1949年 至 1969年间航空客运量的变化情况。\n",
    "plt.plot('date', 'traffic', data = df)：其中前两项即为df的表头\n",
    "\n",
    "# Import Data\n",
    "df = pd.read_csv('AirPassengers.csv')\n",
    "\n",
    "# Draw Plot\n",
    "plt.figure(figsize=(16,10), dpi= 80)\n",
    "plt.plot('date', 'traffic', data = df, color='tab:red')\n",
    "\n",
    "# Decoration\n",
    "plt.ylim(50, 750)\n",
    "xtick_location = df.index.tolist()[::12]\n",
    "xtick_labels = [x[-4:] for x in df.date.tolist()[::12]]\n",
    "plt.xticks(ticks=xtick_location, labels=xtick_labels, rotation=0, fontsize=12, horizontalalignment='center', alpha=.7)\n",
    "plt.yticks(fontsize=12, alpha=.7)\n",
    "plt.title(\"Air Passengers Traffic (1949 - 1969)\", fontsize=22)\n",
    "plt.grid(axis='both', alpha=.3)\n",
    "\n",
    "# Remove borders\n",
    "plt.gca().spines[\"top\"].set_alpha(0.0)    \n",
    "plt.gca().spines[\"bottom\"].set_alpha(0.3)\n",
    "plt.gca().spines[\"right\"].set_alpha(0.0)    \n",
    "plt.gca().spines[\"left\"].set_alpha(0.3)   \n",
    "plt.show()\n",
    "\n",
    "带波峰波谷标记的时序图 （Time Series with Peaks and Troughs Annotated）\n",
    "下面的时间序列绘制了所有峰值和低谷，并注释了所选特殊事件的发生。\n",
    "\n",
    "# Import Data\n",
    "df = pd.read_csv('AirPassengers.csv')\n",
    "\n",
    "# Get the Peaks and Troughs\n",
    "data = df['traffic'].values\n",
    "doublediff = np.diff(np.sign(np.diff(data)))\n",
    "peak_locations = np.where(doublediff == -2)[0] + 1\n",
    "\n",
    "doublediff2 = np.diff(np.sign(np.diff(-1*data)))\n",
    "trough_locations = np.where(doublediff2 == -2)[0] + 1\n",
    "\n",
    "# Draw Plot\n",
    "plt.figure(figsize=(16,10), dpi= 80)\n",
    "plt.plot('date', 'traffic', data=df, color='tab:blue', label='Air Traffic')\n",
    "plt.scatter(df.date[peak_locations], df.traffic[peak_locations], marker=mpl.markers.CARETUPBASE, color='tab:green', s=100, label='Peaks')\n",
    "plt.scatter(df.date[trough_locations], df.traffic[trough_locations], marker=mpl.markers.CARETDOWNBASE, color='tab:red', s=100, label='Troughs')\n",
    "\n",
    "# Annotate\n",
    "for t, p in zip(trough_locations[1::5], peak_locations[::3]):\n",
    "    plt.text(df.date[p], df.traffic[p]+15, df.date[p], horizontalalignment='center', color='darkgreen')\n",
    "    plt.text(df.date[t], df.traffic[t]-35, df.date[t], horizontalalignment='center', color='darkred')\n",
    "\n",
    "# Decoration\n",
    "plt.ylim(50,750)\n",
    "xtick_location = df.index.tolist()[::6]\n",
    "xtick_labels = df.date.tolist()[::6]\n",
    "plt.xticks(ticks=xtick_location, labels=xtick_labels, rotation=90, fontsize=12, alpha=.7)\n",
    "plt.title(\"Peak and Troughs of Air Passengers Traffic (1949 - 1969)\", fontsize=22)\n",
    "plt.yticks(fontsize=12, alpha=.7)\n",
    "\n",
    "# Lighten borders\n",
    "plt.gca().spines[\"top\"].set_alpha(.0)\n",
    "plt.gca().spines[\"bottom\"].set_alpha(.3)\n",
    "plt.gca().spines[\"right\"].set_alpha(.0)\n",
    "plt.gca().spines[\"left\"].set_alpha(.3)\n",
    "\n",
    "plt.legend(loc='upper left')\n",
    "plt.grid(axis='y', alpha=.3)\n",
    "plt.show()\n",
    "\n",
    "自相关和部分自相关图 （Autocorrelation (ACF) and Partial Autocorrelation (PACF) Plot）\n",
    "自相关图（ACF图）显示时间序列与其自身滞后的相关性。 每条垂直线（在自相关图上）表示系列与滞后0之间的滞后之间的相关性。图中的蓝色阴影区域是显着性水平。 那些位于蓝线之上的滞后是显着的滞后。\n",
    "\n",
    "那么如何解读呢？\n",
    "\n",
    "对于空乘旅客，我们看到多达14个滞后跨越蓝线，因此非常重要。 这意味着，14年前的航空旅客交通量对今天的交通状况有影响。\n",
    "\n",
    "PACF在另一方面显示了任何给定滞后（时间序列）与当前序列的自相关，但是删除了滞后的贡献。\n",
    "\n",
    "from statsmodels.graphics.tsaplots import plot_acf, plot_pacf\n",
    "\n",
    "# Import Data\n",
    "df = pd.read_csv('AirPassengers.csv')\n",
    "\n",
    "# Draw Plot\n",
    "fig, (ax1, ax2) = plt.subplots(1, 2,figsize=(16,6), dpi= 80)\n",
    "plot_acf(df.traffic.tolist(), ax=ax1, lags=50)\n",
    "plot_pacf(df.traffic.tolist(), ax=ax2, lags=20)\n",
    "\n",
    "# Decorate\n",
    "# lighten the borders\n",
    "ax1.spines[\"top\"].set_alpha(.3); ax2.spines[\"top\"].set_alpha(.3)\n",
    "ax1.spines[\"bottom\"].set_alpha(.3); ax2.spines[\"bottom\"].set_alpha(.3)\n",
    "ax1.spines[\"right\"].set_alpha(.3); ax2.spines[\"right\"].set_alpha(.3)\n",
    "ax1.spines[\"left\"].set_alpha(.3); ax2.spines[\"left\"].set_alpha(.3)\n",
    "\n",
    "# font size of tick labels\n",
    "ax1.tick_params(axis='both', labelsize=12)\n",
    "ax2.tick_params(axis='both', labelsize=12)\n",
    "plt.show()\n",
    "\n",
    "交叉相关图 （Cross Correlation plot）\n",
    "交叉相关图显示了两个时间序列相互之间的滞后。\n",
    "\n",
    "import statsmodels.tsa.stattools as stattools\n",
    "\n",
    "# Import Data\n",
    "df = pd.read_csv('mortality.csv')\n",
    "x = df['mdeaths']\n",
    "y = df['fdeaths']\n",
    "\n",
    "# Compute Cross Correlations\n",
    "ccs = stattools.ccf(x, y)[:100]\n",
    "nlags = len(ccs)\n",
    "\n",
    "# Compute the Significance level\n",
    "# ref: https://stats.stackexchange.com/questions/3115/cross-correlation-significance-in-r/3128#3128\n",
    "conf_level = 2 / np.sqrt(nlags)\n",
    "\n",
    "# Draw Plot\n",
    "plt.figure(figsize=(12,7), dpi= 80)\n",
    "\n",
    "plt.hlines(0, xmin=0, xmax=100, color='gray')  # 0 axis\n",
    "plt.hlines(conf_level, xmin=0, xmax=100, color='gray')\n",
    "plt.hlines(-conf_level, xmin=0, xmax=100, color='gray')\n",
    "\n",
    "plt.bar(x=np.arange(len(ccs)), height=ccs, width=.3)\n",
    "\n",
    "# Decoration\n",
    "plt.title('$Cross\\; Correlation\\; Plot:\\; mdeaths\\; vs\\; fdeaths$', fontsize=22)\n",
    "plt.xlim(0,len(ccs))\n",
    "plt.show()\n",
    "\n",
    "时间序列分解图 （Time Series Decomposition Plot）\n",
    "时间序列分解图显示时间序列分解为趋势，季节和残差分量。\n",
    "\n",
    "from statsmodels.tsa.seasonal import seasonal_decompose\n",
    "from dateutil.parser import parse\n",
    "\n",
    "# Import Data\n",
    "df = pd.read_csv('AirPassengers.csv')\n",
    "dates = pd.DatetimeIndex([parse(d).strftime('%Y-%m-01') for d in df['date']])\n",
    "df.set_index(dates, inplace=True)\n",
    "\n",
    "# Decompose \n",
    "result = seasonal_decompose(df['traffic'], model='multiplicative')\n",
    "\n",
    "# Plot\n",
    "plt.rcParams.update({'figure.figsize': (10,10)})\n",
    "result.plot().suptitle('Time Series Decomposition of Air Passengers')\n",
    "plt.show()\n",
    "\n",
    "多个时间序列 （Multiple Time Series）\n",
    "您可以绘制多个时间序列，在同一图表上测量相同的值，如下所示。\n",
    "\n",
    "# Import Data\n",
    "df = pd.read_csv('mortality.csv')\n",
    "\n",
    "# Define the upper limit, lower limit, interval of Y axis and colors\n",
    "y_LL = 100\n",
    "y_UL = int(df.iloc[:, 1:].max().max()*1.1)\n",
    "y_interval = 400\n",
    "mycolors = ['tab:red', 'tab:blue', 'tab:green', 'tab:orange']    \n",
    "\n",
    "# Draw Plot and Annotate\n",
    "fig, ax = plt.subplots(1,1,figsize=(16, 9), dpi= 80)    \n",
    "\n",
    "columns = df.columns[1:]  \n",
    "for i, column in enumerate(columns):\n",
    "    plt.plot(df.date.values, df[column].values, lw=1.5, color=mycolors[i])    \n",
    "    plt.text(df.shape[0]+1, df[column].values[-1], column, fontsize=14, color=mycolors[i])\n",
    "\n",
    "# Draw Tick lines  \n",
    "for y in range(y_LL, y_UL, y_interval):    \n",
    "    plt.hlines(y, xmin=0, xmax=71, colors='black', alpha=0.3, linestyles=\"--\", lw=0.5)\n",
    "\n",
    "# Decorations    \n",
    "plt.tick_params(axis=\"both\", which=\"both\", bottom=False, top=False,    \n",
    "                labelbottom=True, left=False, right=False, labelleft=True)        \n",
    "\n",
    "# Lighten borders\n",
    "plt.gca().spines[\"top\"].set_alpha(.3)\n",
    "plt.gca().spines[\"bottom\"].set_alpha(.3)\n",
    "plt.gca().spines[\"right\"].set_alpha(.3)\n",
    "plt.gca().spines[\"left\"].set_alpha(.3)\n",
    "\n",
    "plt.title('Number of Deaths from Lung Diseases in the UK (1974-1979)', fontsize=22)\n",
    "plt.yticks(range(y_LL, y_UL, y_interval), [str(y) for y in range(y_LL, y_UL, y_interval)], fontsize=12)    \n",
    "plt.xticks(range(0, df.shape[0], 12), df.date.values[::12], horizontalalignment='left', fontsize=12)    \n",
    "plt.ylim(y_LL, y_UL)    \n",
    "plt.xlim(-2, 80)    \n",
    "plt.show()\n",
    "\n",
    "使用辅助 Y 轴来绘制不同范围的图形 （Plotting with different scales using secondary Y axis）\n",
    "如果要显示在同一时间点测量两个不同数量的两个时间序列，则可以在右侧的辅助Y轴上再绘制第二个系列。\n",
    "\n",
    "# Import Data\n",
    "df = pd.read_csv(\"economics.csv\")\n",
    "\n",
    "x = df['date']\n",
    "y1 = df['psavert']\n",
    "y2 = df['unemploy']\n",
    "\n",
    "# Plot Line1 (Left Y Axis)\n",
    "fig, ax1 = plt.subplots(1,1,figsize=(16,9), dpi= 80)\n",
    "ax1.plot(x, y1, color='tab:red')\n",
    "\n",
    "# Plot Line2 (Right Y Axis)\n",
    "ax2 = ax1.twinx()  # instantiate a second axes that shares the same x-axis\n",
    "ax2.plot(x, y2, color='tab:blue')\n",
    "\n",
    "# Decorations\n",
    "# ax1 (left Y axis)\n",
    "ax1.set_xlabel('Year', fontsize=20)\n",
    "ax1.tick_params(axis='x', rotation=0, labelsize=12)\n",
    "ax1.set_ylabel('Personal Savings Rate', color='tab:red', fontsize=20)\n",
    "ax1.tick_params(axis='y', rotation=0, labelcolor='tab:red' )\n",
    "ax1.grid(alpha=.4)\n",
    "\n",
    "# ax2 (right Y axis)\n",
    "ax2.set_ylabel(\"# Unemployed (1000's)\", color='tab:blue', fontsize=20)\n",
    "ax2.tick_params(axis='y', labelcolor='tab:blue')\n",
    "ax2.set_xticks(np.arange(0, len(x), 60))\n",
    "ax2.set_xticklabels(x[::60], rotation=90, fontdict={'fontsize':10})\n",
    "ax2.set_title(\"Personal Savings Rate vs Unemployed: Plotting in Secondary Y Axis\", fontsize=22)\n",
    "fig.tight_layout()\n",
    "plt.show()\n",
    "\n",
    "带有误差带的时间序列 （Time Series with Error Bands）\n",
    "如果您有一个时间序列数据集，每个时间点（日期/时间戳）有多个观测值，则可以构建带有误差带的时间序列。 您可以在下面看到一些基于每天不同时间订单的示例。 另一个关于45天持续到达的订单数量的例子。\n",
    "\n",
    "在该方法中，订单数量的平均值由白线表示。 并且计算95％置信区间并围绕均值绘制。\n",
    "\n",
    "from scipy.stats import sem\n",
    "\n",
    "# Import Data\n",
    "df = pd.read_csv(\"user_orders_hourofday.csv\")\n",
    "df_mean = df.groupby('order_hour_of_day').quantity.mean()\n",
    "df_se = df.groupby('order_hour_of_day').quantity.apply(sem).mul(1.96)\n",
    "\n",
    "# Plot\n",
    "plt.figure(figsize=(16,10), dpi= 80)\n",
    "plt.ylabel(\"# Orders\", fontsize=16)  \n",
    "x = df_mean.index\n",
    "plt.plot(x, df_mean, color=\"white\", lw=2) \n",
    "plt.fill_between(x, df_mean - df_se, df_mean + df_se, color=\"#3F5D7D\")  \n",
    "\n",
    "# Decorations\n",
    "# Lighten borders\n",
    "plt.gca().spines[\"top\"].set_alpha(0)\n",
    "plt.gca().spines[\"bottom\"].set_alpha(1)\n",
    "plt.gca().spines[\"right\"].set_alpha(0)\n",
    "plt.gca().spines[\"left\"].set_alpha(1)\n",
    "plt.xticks(x[::2], [str(d) for d in x[::2]] , fontsize=12)\n",
    "plt.title(\"User Orders by Hour of Day (95% confidence)\", fontsize=22)\n",
    "plt.xlabel(\"Hour of Day\")\n",
    "\n",
    "s, e = plt.gca().get_xlim()\n",
    "plt.xlim(s, e)\n",
    "\n",
    "# Draw Horizontal Tick lines  \n",
    "for y in range(8, 20, 2):    \n",
    "    plt.hlines(y, xmin=s, xmax=e, colors='black', alpha=0.5, linestyles=\"--\", lw=0.5)\n",
    "\n",
    "plt.show()\n",
    "\n",
    "# \"Data Source: https://www.kaggle.com/olistbr/brazilian-ecommerce#olist_orders_dataset.csv\"\n",
    "from dateutil.parser import parse\n",
    "from scipy.stats import sem\n",
    "\n",
    "# Import Data\n",
    "df_raw = pd.read_csv('https://raw.githubusercontent.com/selva86/datasets/master/orders_45d.csv', \n",
    "                     parse_dates=['purchase_time', 'purchase_date'])\n",
    "\n",
    "# Prepare Data: Daily Mean and SE Bands\n",
    "df_mean = df_raw.groupby('purchase_date').quantity.mean()\n",
    "df_se = df_raw.groupby('purchase_date').quantity.apply(sem).mul(1.96)\n",
    "\n",
    "# Plot\n",
    "plt.figure(figsize=(16,10), dpi= 80)\n",
    "plt.ylabel(\"# Daily Orders\", fontsize=16)  \n",
    "x = [d.date().strftime('%Y-%m-%d') for d in df_mean.index]\n",
    "plt.plot(x, df_mean, color=\"white\", lw=2) \n",
    "plt.fill_between(x, df_mean - df_se, df_mean + df_se, color=\"#3F5D7D\")  \n",
    "\n",
    "# Decorations\n",
    "# Lighten borders\n",
    "plt.gca().spines[\"top\"].set_alpha(0)\n",
    "plt.gca().spines[\"bottom\"].set_alpha(1)\n",
    "plt.gca().spines[\"right\"].set_alpha(0)\n",
    "plt.gca().spines[\"left\"].set_alpha(1)\n",
    "plt.xticks(x[::6], [str(d) for d in x[::6]] , fontsize=12)\n",
    "plt.title(\"Daily Order Quantity of Brazilian Retail with Error Bands (95% confidence)\", fontsize=20)\n",
    "\n",
    "# Axis limits\n",
    "s, e = plt.gca().get_xlim()\n",
    "plt.xlim(s, e-2)\n",
    "plt.ylim(4, 10)\n",
    "\n",
    "# Draw Horizontal Tick lines  \n",
    "for y in range(5, 10, 1):    \n",
    "    plt.hlines(y, xmin=s, xmax=e, colors='black', alpha=0.5, linestyles=\"--\", lw=0.5)\n",
    "\n",
    "plt.show()\n",
    "\n",
    "堆积面积图 （Stacked Area Chart）\n",
    "堆积面积图可以直观地显示多个时间序列的贡献程度，因此很容易相互比较。\n",
    "\n",
    "# Import Data\n",
    "df = pd.read_csv('nightvisitors.csv')\n",
    "\n",
    "# Decide Colors \n",
    "mycolors = ['tab:red', 'tab:blue', 'tab:green', 'tab:orange', 'tab:brown', 'tab:grey', 'tab:pink', 'tab:olive']      \n",
    "\n",
    "# Draw Plot and Annotate\n",
    "fig, ax = plt.subplots(1,1,figsize=(16, 9), dpi= 80)\n",
    "columns = df.columns[1:]\n",
    "labs = columns.values.tolist()\n",
    "\n",
    "# Prepare data\n",
    "x  = df['yearmon'].values.tolist()\n",
    "y0 = df[columns[0]].values.tolist()\n",
    "y1 = df[columns[1]].values.tolist()\n",
    "y2 = df[columns[2]].values.tolist()\n",
    "y3 = df[columns[3]].values.tolist()\n",
    "y4 = df[columns[4]].values.tolist()\n",
    "y5 = df[columns[5]].values.tolist()\n",
    "y6 = df[columns[6]].values.tolist()\n",
    "y7 = df[columns[7]].values.tolist()\n",
    "y = np.vstack([y0, y2, y4, y6, y7, y5, y1, y3])\n",
    "\n",
    "# Plot for each column\n",
    "labs = columns.values.tolist()\n",
    "ax = plt.gca()\n",
    "ax.stackplot(x, y, labels=labs, colors=mycolors, alpha=0.8)\n",
    "\n",
    "# Decorations\n",
    "ax.set_title('Night Visitors in Australian Regions', fontsize=18)\n",
    "ax.set(ylim=[0, 100000])\n",
    "ax.legend(fontsize=10, ncol=4)\n",
    "plt.xticks(x[::5], fontsize=10, horizontalalignment='center')\n",
    "plt.yticks(np.arange(10000, 100000, 20000), fontsize=10)\n",
    "plt.xlim(x[0], x[-1])\n",
    "\n",
    "# Lighten borders\n",
    "plt.gca().spines[\"top\"].set_alpha(0)\n",
    "plt.gca().spines[\"bottom\"].set_alpha(.3)\n",
    "plt.gca().spines[\"right\"].set_alpha(0)\n",
    "plt.gca().spines[\"left\"].set_alpha(.3)\n",
    "\n",
    "plt.show()\n",
    "\n",
    "未堆积的面积图 （Area Chart UnStacked）\n",
    "未堆积面积图用于可视化两个或更多个系列相对于彼此的进度（起伏）。 在下面的图表中，您可以清楚地看到随着失业中位数持续时间的增加，个人储蓄率会下降。 未堆积面积图表很好地展示了这种现象。\n",
    "\n",
    "# Import Data\n",
    "df = pd.read_csv(\"economics.csv\")\n",
    "\n",
    "# Prepare Data\n",
    "x = df['date'].values.tolist()\n",
    "y1 = df['psavert'].values.tolist()\n",
    "y2 = df['uempmed'].values.tolist()\n",
    "mycolors = ['tab:red', 'tab:blue', 'tab:green', 'tab:orange', 'tab:brown', 'tab:grey', 'tab:pink', 'tab:olive']      \n",
    "columns = ['psavert', 'uempmed']\n",
    "\n",
    "# Draw Plot \n",
    "fig, ax = plt.subplots(1, 1, figsize=(16,9), dpi= 80)\n",
    "ax.fill_between(x, y1=y1, y2=0, label=columns[1], alpha=0.5, color=mycolors[1], linewidth=2)\n",
    "ax.fill_between(x, y1=y2, y2=0, label=columns[0], alpha=0.5, color=mycolors[0], linewidth=2)\n",
    "\n",
    "# Decorations\n",
    "ax.set_title('Personal Savings Rate vs Median Duration of Unemployment', fontsize=18)\n",
    "ax.set(ylim=[0, 30])\n",
    "ax.legend(loc='best', fontsize=12)\n",
    "plt.xticks(x[::50], fontsize=10, horizontalalignment='center')\n",
    "plt.yticks(np.arange(2.5, 30.0, 2.5), fontsize=10)\n",
    "plt.xlim(-10, x[-1])\n",
    "\n",
    "# Draw Tick lines  \n",
    "for y in np.arange(2.5, 30.0, 2.5):    \n",
    "    plt.hlines(y, xmin=0, xmax=len(x), colors='black', alpha=0.3, linestyles=\"--\", lw=0.5)\n",
    "\n",
    "# Lighten borders\n",
    "plt.gca().spines[\"top\"].set_alpha(0)\n",
    "plt.gca().spines[\"bottom\"].set_alpha(.3)\n",
    "plt.gca().spines[\"right\"].set_alpha(0)\n",
    "plt.gca().spines[\"left\"].set_alpha(.3)\n",
    "plt.show()\n",
    "\n",
    "日历热力图 （Calendar Heat Map）\n",
    "与时间序列相比，日历地图是可视化基于时间的数据的备选和不太优选的选项。 虽然可以在视觉上吸引人，但数值并不十分明显。 然而，它可以很好地描绘极端值和假日效果。（需要安装 calmap 库）\n",
    "\n",
    "import numpy as np; np.random.seed(sum(map(ord, 'calplot')))\n",
    "import pandas as pd\n",
    "import calplot\n",
    "\n",
    "all_days = pd.date_range('1/1/2019', periods=730, freq='D')\n",
    "days = np.random.choice(all_days, 500)\n",
    "events = pd.Series(np.random.randn(len(days)), index=days)\n",
    "calplot.calplot(events, cmap='YlGn')\n",
    "(<Figure size 900x244.8 with 3 Axes>,\n",
    " array([<AxesSubplot:ylabel='2019'>, <AxesSubplot:ylabel='2020'>],\n",
    "       dtype=object))\n",
    "\n",
    "季节图 （Seasonal Plot）\n",
    "季节图可用于比较上一季中同一天（年/月/周等）的时间序列。\n",
    "\n",
    "from dateutil.parser import parse \n",
    "\n",
    "# Import Data\n",
    "df = pd.read_csv('AirPassengers.csv')\n",
    "\n",
    "# Prepare data\n",
    "df['year'] = [parse(d).year for d in df.date]\n",
    "df['month'] = [parse(d).strftime('%b') for d in df.date]\n",
    "years = df['year'].unique()\n",
    "\n",
    "# Draw Plot\n",
    "mycolors = ['tab:red', 'tab:blue', 'tab:green', 'tab:orange', 'tab:brown', 'tab:grey', 'tab:pink', 'tab:olive', 'deeppink', 'steelblue', 'firebrick', 'mediumseagreen']      \n",
    "plt.figure(figsize=(16,10), dpi= 80)\n",
    "\n",
    "for i, y in enumerate(years):\n",
    "    plt.plot('month', 'traffic', data=df.loc[df.year==y, :], color=mycolors[i], label=y)\n",
    "    plt.text(df.loc[df.year==y, :].shape[0]-.9, df.loc[df.year==y, 'traffic'][-1:].values[0], y, fontsize=12, color=mycolors[i])\n",
    "\n",
    "# Decoration\n",
    "plt.ylim(50,750)\n",
    "plt.xlim(-0.3, 11)\n",
    "plt.ylabel('$Air Traffic$')\n",
    "plt.yticks(fontsize=12, alpha=.7)\n",
    "plt.title(\"Monthly Seasonal Plot: Air Passengers Traffic (1949 - 1969)\", fontsize=22)\n",
    "plt.grid(axis='y', alpha=.3)\n",
    "\n",
    "# Remove borders\n",
    "plt.gca().spines[\"top\"].set_alpha(0.0)    \n",
    "plt.gca().spines[\"bottom\"].set_alpha(0.5)\n",
    "plt.gca().spines[\"right\"].set_alpha(0.0)    \n",
    "plt.gca().spines[\"left\"].set_alpha(0.5)   \n",
    "plt.legend(loc='upper right', ncol=2, fontsize=12)\n",
    "plt.show()\n",
    "\n",
    "7、分组 （Groups）\n",
    "树状图 （Dendrogram）\n",
    "树形图基于给定的距离度量将相似的点组合在一起，并基于点的相似性将它们组织在树状链接中。\n",
    "\n",
    "import scipy.cluster.hierarchy as shc\n",
    "\n",
    "# Import Data\n",
    "df = pd.read_csv('USArrests.csv')\n",
    "\n",
    "# Plot\n",
    "plt.figure(figsize=(16, 10), dpi= 80)  \n",
    "plt.title(\"USArrests Dendograms\", fontsize=22)  \n",
    "dend = shc.dendrogram(shc.linkage(df[['Murder', 'Assault', 'UrbanPop', 'Rape']], method='ward'), labels=df.State.values, color_threshold=100)  \n",
    "plt.xticks(fontsize=12)\n",
    "plt.show()\n",
    "\n",
    "簇状图 （Cluster Plot）\n",
    "簇状图 （Cluster Plot）可用于划分属于同一群集的点。 下面是根据USArrests数据集将美国各州分为5组的代表性示例。 此图使用“谋杀”和“攻击”列作为X和Y轴。 或者，您可以将第一个到主要组件用作X轴和Y轴。\n",
    "\n",
    "from sklearn.cluster import AgglomerativeClustering\n",
    "from scipy.spatial import ConvexHull\n",
    "\n",
    "# Import Data\n",
    "df = pd.read_csv('USArrests.csv')\n",
    "\n",
    "# Agglomerative Clustering\n",
    "cluster = AgglomerativeClustering(n_clusters=5, affinity='euclidean', linkage='ward')  \n",
    "cluster.fit_predict(df[['Murder', 'Assault', 'UrbanPop', 'Rape']])  \n",
    "\n",
    "# Plot\n",
    "plt.figure(figsize=(14, 10), dpi= 80)  \n",
    "plt.scatter(df.iloc[:,0], df.iloc[:,1], c=cluster.labels_, cmap='tab10')  \n",
    "\n",
    "# Encircle\n",
    "def encircle(x,y, ax=None, **kw):\n",
    "    if not ax: ax=plt.gca()\n",
    "    p = np.c_[x,y]\n",
    "    hull = ConvexHull(p)\n",
    "    poly = plt.Polygon(p[hull.vertices,:], **kw)\n",
    "    ax.add_patch(poly)\n",
    "\n",
    "# Draw polygon surrounding vertices    \n",
    "encircle(df.loc[cluster.labels_ == 0, 'Murder'], df.loc[cluster.labels_ == 0, 'Assault'], ec=\"k\", fc=\"gold\", alpha=0.2, linewidth=0)\n",
    "encircle(df.loc[cluster.labels_ == 1, 'Murder'], df.loc[cluster.labels_ == 1, 'Assault'], ec=\"k\", fc=\"tab:blue\", alpha=0.2, linewidth=0)\n",
    "encircle(df.loc[cluster.labels_ == 2, 'Murder'], df.loc[cluster.labels_ == 2, 'Assault'], ec=\"k\", fc=\"tab:red\", alpha=0.2, linewidth=0)\n",
    "encircle(df.loc[cluster.labels_ == 3, 'Murder'], df.loc[cluster.labels_ == 3, 'Assault'], ec=\"k\", fc=\"tab:green\", alpha=0.2, linewidth=0)\n",
    "encircle(df.loc[cluster.labels_ == 4, 'Murder'], df.loc[cluster.labels_ == 4, 'Assault'], ec=\"k\", fc=\"tab:orange\", alpha=0.2, linewidth=0)\n",
    "\n",
    "# Decorations\n",
    "plt.xlabel('Murder'); plt.xticks(fontsize=12)\n",
    "plt.ylabel('Assault'); plt.yticks(fontsize=12)\n",
    "plt.title('Agglomerative Clustering of USArrests (5 Groups)', fontsize=22)\n",
    "plt.show()\n",
    "\n",
    "安德鲁斯曲线 （Andrews Curve）\n",
    "安德鲁斯曲线有助于可视化是否存在基于给定分组的数字特征的固有分组。 如果要素（数据集中的列）无法区分组（cyl），那么这些线将不会很好地隔离，如下所示。\n",
    "\n",
    "from pandas.plotting import andrews_curves\n",
    "\n",
    "# Import\n",
    "df = pd.read_csv(\"mtcars.csv\")\n",
    "df.drop(['cars', 'carname'], axis=1, inplace=True)\n",
    "\n",
    "# Plot\n",
    "plt.figure(figsize=(12,9), dpi= 80)\n",
    "andrews_curves(df, 'cyl', colormap='Set1')\n",
    "\n",
    "# Lighten borders\n",
    "plt.gca().spines[\"top\"].set_alpha(0)\n",
    "plt.gca().spines[\"bottom\"].set_alpha(.3)\n",
    "plt.gca().spines[\"right\"].set_alpha(0)\n",
    "plt.gca().spines[\"left\"].set_alpha(.3)\n",
    "\n",
    "plt.title('Andrews Curves of mtcars', fontsize=22)\n",
    "plt.xlim(-3,3)\n",
    "plt.grid(alpha=0.3)\n",
    "plt.xticks(fontsize=12)\n",
    "plt.yticks(fontsize=12)\n",
    "plt.show()\n",
    "\n",
    "平行坐标 （Parallel Coordinates）\n",
    "平行坐标有助于可视化特征是否有助于有效地隔离组。 如果实现隔离，则该特征可能在预测该组时非常有用。\n",
    "\n",
    "from pandas.plotting import parallel_coordinates\n",
    "\n",
    "# Import Data\n",
    "df_final = pd.read_csv(\"diamonds_filter.csv\")\n",
    "\n",
    "# Plot\n",
    "plt.figure(figsize=(12,9), dpi= 80)\n",
    "parallel_coordinates(df_final, 'cut', colormap='Dark2')\n",
    "\n",
    "# Lighten borders\n",
    "plt.gca().spines[\"top\"].set_alpha(0)\n",
    "plt.gca().spines[\"bottom\"].set_alpha(.3)\n",
    "plt.gca().spines[\"right\"].set_alpha(0)\n",
    "plt.gca().spines[\"left\"].set_alpha(.3)\n",
    "\n",
    "plt.title('Parallel Coordinated of Diamonds', fontsize=22)\n",
    "plt.grid(alpha=0.3)\n",
    "plt.xticks(fontsize=12)\n",
    "plt.yticks(fontsize=12)\n",
    "plt.show()\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "pytorch_cuda",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
